Tutorial 4: Dessiner du texte
(Painting with Text)

Dans ce tutorial, nous apprendrons comment "peindre" le texte dans 'le secteur client' (la zone de travail) d'une fenêtre. Nous verrons aussi tout le contexte autour de cette fonction. Vous pouvez télécharger le code source ici.

Théorie:

Le texte dans Windows est un type d'objet du GUI (NdT : Graphic User Interface). Chaque caractère est composé de plusieurs pixels (les points) qui sont regroupés dans un ordre particulier. C'est pourquoi on peut appeler "painting" au lieu de writting". Normalement, vous peignez le texte dans votre propre secteur client (en réalité, vous pouvez peindre le secteur client extérieur mais c'est une autre histoire). L'affichage du texte sur l'écran sous Windows diffère résolument du DOS. Sous DOS, la dimension de l'écran est 80x25. Mais sous Windows, l'écran est partagé selon plusieurs programmes. Des règles doivent être prises pour éviter que certains programmes s'affichent par dessus l'application d'autres programmes. Windows contrôle ceci en limitant la peinture du secteur de chaque fenêtre à son propre secteur client seulement. La taille du secteur client d'une fenêtre n'est pas aussi rigide. L'utilisateur peut changer sa taille n'importe quand. Donc vous devez déterminer les dimensions de votre propre secteur client.
Avant que vous ne puissiez peindre quelque chose sur le secteur client, vous devez demander la permission à Windows. C'est ça, vous n'avez pas le contrôle absolu de l'écran comme si vous étiez sous DOS. Vous devez demander à Windows la permission de peindre votre propre secteur client. Windows déterminera la taille de votre secteur client, la taille des caractères, les couleurs et les autres GDI attribués et ensuite renvoie à votre programme l'Handle du 'contexte de ce dispositif' (de tout ça). Vous pourrez alors employer le 'contexte de dispositif' comme un passeport à la peinture sur votre 'secteur client'.
C'est quoi le 'contexte de dispositif' ? C'est juste une structure de données conçue à intérieure de Windows (pas accessible pour nous). Un 'contexte de dispositif' est associé à un dispositif particulier, comme une imprimante ou l'exposition vidéo. Pour une exposition vidéo, un contexte de dispositif est d'habitude associé à une fenêtre particulière en exposition.
Quelques-unes des valeurs dans le contexte de dispositif sont des attributs graphiques comme des couleurs, la font etc. Ce sont des valeurs par défaut que vous pouvez changer à volonté. Il est possible pour réduire la charge de travail de spécifier ces attributs dans chaques appels de fonction GDI.
Vous pouvez vous représenter un 'contexte de dispositif' comme un 'environnement par défaut' mis à votre disposition par Windows. Vous pouvez ne pas tenir compte de certaines éléments par défaut puis y revenir plus tard si vous le souhaitez.
Quand un programme a besoin de peindre, il doit obtenir un 'Handle de contexte de dispositif'. Normalement, il y a plusieurs façons de l'obtenir. Rappeler vous une chose, après avoir obtenu un 'Handle de contexte de dispositif', vous devez le sortir pendant le traitement d'un simple message. Ne récupérez pas un Handle en réponse à un message, transférez-le en réponse à un autre message.
Windows envoie des messages WM_PAINT à une fenêtre pour lui indiquer que c'est maintenant le temps de repeindre son secteur client. Windows ne sauve pas le contenu de secteur client de la fenêtre, mais au lieu de cela, quand une situation arrive et garantit une nouvelle peinture de secteur client (comme quand une fenêtre a été couverte par une autre et vient la recouvrir), Windows met le message WM_PAINT dans la file d'attente des messages de cette fenêtre. C'est à cette fenêtre de repeindre son propre secteur de client. Vous devez réunir toute l'information qui sert à repeindre votre secteur client dans la section WM_PAINT de votre procédure de fenêtre, ainsi la procédure de fenêtre (la procédure qui gère l'affichage et le comportement de votre fenêtre) pourra repeindre le secteur client quand le message WM_PAINT arrivera.
Un autre concept que vous devez prendre en compte est une petite zone rectangle. Windows définit un rectangle invalide comme le plus petit secteur rectangulaire dans le secteur client qui a besoin d'être repeint (rafraîchi) . Quand Windows détecte un rectangle invalide dans le secteur client d'une fenêtre, il envoie le message WM_PAINT à cette fenêtre. En réponse à ce message WM_PAINT, la fenêtre peut obtenir une structure 'Paintstruct' qui contient, entre autre, les coordonnées du rectangle invalide. Vous appelez 'BeginPaint' en réponse au message WM_PAINT pour valider le rectangle invalide. Si vous ne traitez pas le message WM_PAINT, alors vous devez appeler DefWindowProc ou ValidateRect pour valider le rectangle invalide sinon à plusieurs reprises Windows vous enverra le message WM_PAINT. Laissez tombé ça, c'est trop confus.
Voici ci-dessous les étapes que vous devez exécuter en réponse à un message WM_PAINT : Notez que vous ne devez pas explicitement valider le rectangle invalide. C'est le Call BeginPaint qui s'en occupe automatiquement. Entre la paire de BeginPaint-Endpaint, vous pouvez appeler n'importe quelles fonctions du GDI pour peindre votre secteur client. Presque tous exigent l' 'handle du contexte de dispositif' en tant que paramètre.

Contenu:

Nous allons écrire un programme qui fait apparaître le texte " Win32 assembly is great and easy!" au le centre du secteur client.
 
.386
.model flat,stdcall
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.DATA
ClassName db "SimpleWinClass",0
AppName  db "Our First Window",0
OurText  db "Win32 assembly is great and easy!",0

.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.CODE
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke GetCommandLine
    mov CommandLine,eax

    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND
    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
        .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
        .ENDW
        mov     eax,msg.wParam
        ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL rect:RECT
    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_PAINT
        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke GetClientRect,hWnd, ADDR rect
        invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
                DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,hWnd, ADDR ps
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor   eax, eax
    ret
WndProc endp
end start

Analyse:

Le code dans sa grande majorité est le même que celui de l'exemple du Tutorial 3. J'expliquerai donc seulement les changements importants.

    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT
    LOCAL rect:RECT

Voici les variables locales qui sont employées par des fonctions du GDI dans notre section WM_PAINT. 'Hdc' est employé pour stocker l'handle du contexte de dispositif renvoyé après l'appel de la fonction BeginPaint. 'ps' est une structure de PAINTSTRUCT. Normalement ce n'est pas vous qui donnez une valeur à ps. On le passe à la fonction BeginPaint et Windows lui donne la valeur appropriée. De la même façon, vous passez 'ps' à la fonction EndPaint quand vous finissez de repeindre le secteur client. 'rect' est une structure rect définie comme suit :
 

RECT Struct
    left           LONG ?
    top           LONG ?
    right        LONG ?
    bottom    LONG ?
RECT ends
Left et top sont les coordonnées du coin supérieur gauche d'un rectangle Right and bottom sont les coordonnées du coin inférieur droit. Une chose à se souvenir : l'origine des axes x-y est au coin supérieur gauche du secteur client. Donc le point y=10 est AU-DESSOUS du point y=0.

        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke GetClientRect,hWnd, ADDR rect
        invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
                DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,hWnd, ADDR ps

En réponse au message WM_PAINT, vous appelez BeginPaint avec la structure PAINTSTRUCT non initialisée et l'handle de la fenêtre que vous souhaitez peindre, comme paramètres. Après un appel couronné de succès, eax contient l'Handle du contexte de dispositif. Ensuite vous appelez GetClientRect pour retrouver la dimension du secteur client. La dimension est renvoyée dans la variable rect que vous passez à DrawText comme un de ses paramètres. La syntaxe de DrawText est :

DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD

DrawText est une production de texte de haut niveau créé par une fonction API. Il manipule quelques renseignements peu coutumiers tels que le 'word wrap' (l'enveloppe autour des mots), ou le 'centering' (l'allignement au centre) etc… que vous pourriez appliquez sur le texte que vous souhaitez peindre. Son équivalent de bas niveau, TextOut, sera examiné dans le Tutorial suivant. DrawText définit un texte pour qu'il loge dans les limites d'un rectangle. Il emploie la fonte (la taille, la police) actuellement choisie, la couleur et le fond (dans le contexte de dispositif) pour dessiner le texte. Les lignes sont encadrées pour rester dans les limites du rectangle. Il rend la hauteur du texte produit dans les unités du dispositif, qui dans notre cas sont en pixels. Regardons de plus près ces paramètres :

Après que vous ayez fini de repeindre le secteur client, vous devez appeler la fonction EndPaint pour libérer l'handle du contexte de dispositif.
Voilà. Nous pouvons récapituler les points importants:
[Iczelion's Win32 Assembly HomePage]


Traduit par Morgatte